﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection;
using Common.Net.DesignByContract;
using ClearUnit.TestRunners.Results;
using ClearUnit.TestRunners.Starts;
using Common.Net.Utilities;
using System.Diagnostics;

namespace ClearUnit.TestRunners
{
    public sealed class MethodRunner : TestRunnerBase<MethodRunResult>
    {
        readonly MethodInfo _method;
        readonly bool _explicitRun;

        public MethodRunner(MethodInfo method, bool explicitRun, params Action<TestRunInfo>[] interestedListeners)
            : base(interestedListeners)
        {
            RequireArg.NotNull(() => method);

            _method = method;
            _explicitRun = explicitRun;
        }

        protected sealed override MethodRunResult RunInternal()
        {
            MethodRunResult runResult;

            if (!_explicitRun && _method.IsExplicit())
                runResult = new MethodRunResult.Skipped.ExplicitTest(_method);
            else
            {
                OnTestInfoEvent(new MethodRunStart(_method));

                var timer = Stopwatch.StartNew();

                try
                {
                    object returnValue = InvokeMethod(_method);

                    runResult = new MethodRunResult.Run.Passed(_method, timer.Elapsed, returnValue);
                }
                catch (TargetInvocationException ex)
                {
                    runResult = new MethodRunResult.Run.Failed(_method, timer.Elapsed, ex.InnerException);
                }
            }

            OnTestInfoEvent(runResult);
            return runResult;
        }

        static object InvokeMethod(MethodInfo method)
        {
            object instance = null;

            if (!method.IsStatic)
                instance = Activator.CreateInstance(method.ReflectedType);

            return method.Invoke(instance, ArrayHelper<object>.Empty);
        }
    }
}
